/************************************************************************
  Scheme interpreter
  
  Copyright (C) 2010 Marioly Garza 

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this program. If not, see
  <http://www.gnu.org/licenses/>.

************************************************************************/

#ifndef _NATIVE_H_
#define _NATIVE_H_

#include <list>
#include <fstream>
#include <cstdlib>
#include "closure.h"
#include "environment.h"
#include "procedure.h"
#include "sboolean.h"
#include "sstring.h"
#include "util.h"

#define check_argument_type(f, t, a) if (!util::is<t>(a)) \
    throw scheme_exception(util::format("error in "f": expected type "#t", got '%s'.", util::get_type(a)));
#define call_native(p, a, e) ((native*)scheme::vsymbols->get(p))->invoke(a, e)
#define ARG(x) x.front(); x.pop_front();
#define ARG2DOUBLE(x) atof((((sstring*)x.front())->to_string()).c_str()); x.pop_front();

class native : public procedure
{
    public:
        native(std::string id, object* (*proc)(std::list<object*>, environment* env), int min, int max = -1) : procedure(id, NULL, env) {
            this->proc = proc;
            this->min = min;
            this->max = max;
        }

        object* invoke(std::list<object*> args, environment* env);

        std::string to_string()
        { 
            return util::format("#<procedure-%s>", this->id.c_str()); 
        };

        static object* apply(std::list<object*> lst, environment* env);
        static object* car(std::list<object*> lst, environment* env);
        static object* cdr(std::list<object*> lst, environment* env);
        static object* cons(std::list<object*> lst, environment* env);
        static object* define(std::list<object*> lst, environment* env);
        static object* display(std::list<object*> lst, environment* env);
        static object* equal(std::list<object*> lst, environment* env);
        static object* _if(std::list<object*> lst, environment* env);
        static object* lambda(std::list<object*> lst, environment* env);
        static object* list(std::list<object*> lst, environment* env);
        static object* islist(std::list<object*> lst, environment* env);
        static object* isnull(std::list<object*> lst, environment* env);
        static object* length(std::list<object*> lst, environment* env);
        static object* load(std::list<object*> lst, environment* env);
        static object* quote(std::list<object*> lst, environment* env);
        static object* binary_op(std::string op, std::list<object*> lst, environment* env);
    private:
        object* (*proc)(std::list<object*> l, environment* env);
        int min;
        int max;                   
};

#endif
